/*
* Copyright 2010-2013, Sikuli.org
* Released under the MIT License.
*
* modified RaiMan 2013
*/
package org.sikuli.ide;
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.io.*;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.PreferenceChangeEvent;
import java.util.prefs.PreferenceChangeListener;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.swing.*;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.*;
import org.python.util.PythonInterpreter;
import org.sikuli.script.ImageLocator;
import org.sikuli.script.Settings;
import org.sikuli.ide.indentation.PythonIndentation;
import org.sikuli.ide.util.Utils;
import org.sikuli.script.SikuliScriptRunner;
import org.sikuli.script.Debug;
import org.sikuli.script.FileManager;
import org.sikuli.script.Location;
public class EditorPane extends JTextPane implements KeyListener, CaretListener {
private PreferencesUser pref;
private File _editingFile;
private String _srcBundlePath = null;
private boolean _srcBundleTemp = false;
private boolean _dirty = false;
private EditorCurrentLineHighlighter _highlighter;
private EditorUndoManager _undo = null;
private boolean hasErrorHighlight = false;
public boolean showThumbs;
// TODO: move to SikuliDocument ????
private PythonIndentation _indentationLogic;
static Pattern patPngStr = Pattern.compile("(\"[^\"]+?\\.(?i)(png|jpg)\")");
static Pattern patCaptureBtn = Pattern.compile("(\"__CLICK-TO-CAPTURE__\")");
static Pattern patPatternStr = Pattern.compile(
"\\b(Pattern\\s*\\(\".*?\"\\)(\\.\\w+\\([^)]*\\))+)");
static Pattern patRegionStr = Pattern.compile(
"\\b(Region\\s*\\([\\d\\s,]+\\))");
//TODO SikuliToHtmlConverter implement in Java
final static InputStream SikuliToHtmlConverter =
SikuliIDE.class.getResourceAsStream("/scripts/sikuli2html.py");
static String pyConverter =
FileManager.convertStreamToString(SikuliToHtmlConverter);
//TODO SikuliBundleCleaner implement in Java
final static InputStream SikuliBundleCleaner =
SikuliIDE.class.getResourceAsStream("/scripts/clean-dot-sikuli.py");
static String pyBundleCleaner =
FileManager.convertStreamToString(SikuliBundleCleaner);
//TODO what is it for???
private int _caret_last_x = -1;
private boolean _can_update_caret_last_x = true;
//<editor-fold defaultstate="collapsed" desc="Initialization">
public EditorPane() {
pref = PreferencesUser.getInstance();
showThumbs = !pref.getPrefMorePlainText();
setEditorKitForContentType("text/python", new EditorKit());
setContentType("text/python");
initKeyMap();
setTransferHandler(new MyTransferHandler());
_highlighter = new EditorCurrentLineHighlighter(this);
addCaretListener(_highlighter);
setFont(new Font(pref.getFontName(), Font.PLAIN, pref.getFontSize()));
setMargin(new Insets(3, 3, 3, 3));
setBackground(Color.WHITE);
if (!Settings.isMac()) {
setSelectionColor(new Color(170, 200, 255));
}
updateDocumentListeners();
_indentationLogic = new PythonIndentation();
_indentationLogic.setTabWidth(pref.getTabWidth());
pref.addPreferenceChangeListener(new PreferenceChangeListener() {
@Override
public void preferenceChange(PreferenceChangeEvent event) {
if (event.getKey().equals("TAB_WIDTH")) {
_indentationLogic.setTabWidth(Integer.parseInt(event.getNewValue()));
}
}
});
initEditorPane();
}
private void initEditorPane() {
addKeyListener(this);
addCaretListener(this);
}
private void updateDocumentListeners() {
getDocument().addDocumentListener(new DirtyHandler());
getDocument().addUndoableEditListener(getUndoManager());
}
public EditorUndoManager getUndoManager() {
if (_undo == null) {
_undo = new EditorUndoManager();
}
return _undo;
}
public PythonIndentation getIndentationLogic() {
return _indentationLogic;
}
private void initKeyMap() {
InputMap map = this.getInputMap();
int shift = InputEvent.SHIFT_MASK;
int ctrl = InputEvent.CTRL_MASK;
map.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, shift), EditorKit.deIndentAction);
map.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, ctrl), EditorKit.deIndentAction);
}
@Override
public void keyPressed(java.awt.event.KeyEvent ke) {
}
@Override
public void keyReleased(java.awt.event.KeyEvent ke) {
}
@Override
public void keyTyped(java.awt.event.KeyEvent ke) {
//TODO implement code completion * checkCompletion(ke);
}
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="file handling">
public String loadFile() throws IOException {
File file = new SikuliIDEFileChooser(SikuliIDE.getInstance()).load();
if (file == null) {
return null;
}
String fname = Utils.slashify(file.getAbsolutePath(), false);
SikuliIDE ide = SikuliIDE.getInstance();
int i = ide.isAlreadyOpen(fname);
if (i > -1) {
Debug.log(2, "Already open in IDE: " + fname);
return null;
}
loadFile(fname);
return fname;
}
public void loadFile(String filename) throws IOException {
if (filename.endsWith("/")) {
filename = filename.substring(0, filename.length() - 1);
}
setSrcBundle(filename + "/");
_editingFile = findSourceFile(filename);
this.read(new BufferedReader(new InputStreamReader(
new FileInputStream(_editingFile), "UTF8")), null);
updateDocumentListeners();
setDirty(false);
}
private File findSourceFile(String sikuli_dir) {
if (sikuli_dir.endsWith(".sikuli")
|| sikuli_dir.endsWith(".sikuli" + "/")) {
File dir = new File(sikuli_dir);
File[] pys = dir.listFiles(new GeneralFileFilter("py", "Python Source"));
if (pys.length > 1) {
String sikuli_name = dir.getName();
sikuli_name = sikuli_name.substring(0, sikuli_name.lastIndexOf('.'));
for (File f : pys) {
String py_name = f.getName();
py_name = py_name.substring(0, py_name.lastIndexOf('.'));
if (py_name.equals(sikuli_name)) {
return f;
}
}
}
if (pys.length >= 1) {
return pys[0];
}
}
return new File(sikuli_dir);
}
public String saveFile() throws IOException {
if (_editingFile == null) {
return saveAsFile();
} else {
writeSrcFile();
return getCurrentShortFilename();
}
}
public String saveAsFile() throws IOException {
File file = new SikuliIDEFileChooser(SikuliIDE.getInstance()).save();
if (file == null) {
return null;
}
String bundlePath = file.getAbsolutePath();
if (!file.getAbsolutePath().endsWith(".sikuli")) {
bundlePath += ".sikuli";
}
if (FileManager.exists(bundlePath)) {
int res = JOptionPane.showConfirmDialog(
null, SikuliIDEI18N._I("msgFileExists", bundlePath),
SikuliIDEI18N._I("dlgFileExists"), JOptionPane.YES_NO_OPTION);
if (res != JOptionPane.YES_OPTION) {
return null;
}
} else {
FileManager.mkdir(bundlePath);
}
saveAsBundle(bundlePath, (SikuliIDE.getInstance().getCurrentFileTabTitle()));
return getCurrentShortFilename();
}
private void saveAsBundle(String bundlePath, String current) throws IOException {
bundlePath = Utils.slashify(bundlePath, true);
if (_srcBundlePath != null) {
FileManager.xcopy(_srcBundlePath, bundlePath, current);
}
if (_srcBundleTemp) {
FileManager.deleteTempDir(_srcBundlePath);
_srcBundleTemp = false;
}
setSrcBundle(bundlePath);
_editingFile = createSourceFile(bundlePath, ".py");
Debug.log(2, "save to bundle: " + getSrcBundle());
writeSrcFile();
reparse();
}
private File createSourceFile(String bundlePath, String ext) {
if (bundlePath.endsWith(".sikuli")
|| bundlePath.endsWith(".sikuli/")) {
File dir = new File(bundlePath);
String name = dir.getName();
name = name.substring(0, name.lastIndexOf("."));
return new File(bundlePath, name + ext);
}
return new File(bundlePath);
}
private void writeSrcFile() throws IOException {
writeFile(_editingFile.getAbsolutePath());
if (PreferencesUser.getInstance().getAtSaveMakeHTML()) {
convertSrcToHtml(getSrcBundle());
} else {
(new File(_editingFile.getAbsolutePath().replaceFirst("py", "html"))).delete();
}
if (PreferencesUser.getInstance().getAtSaveCleanBundle()) {
cleanBundle(getSrcBundle());
}
setDirty(false);
}
public String exportAsZip() throws IOException, FileNotFoundException {
File file = new SikuliIDEFileChooser(SikuliIDE.getInstance()).export();
if (file == null) {
return null;
}
String zipPath = file.getAbsolutePath();
String srcName = file.getName();
if (!file.getAbsolutePath().endsWith(".skl")) {
zipPath += ".skl";
} else {
srcName = srcName.substring(0, srcName.lastIndexOf('.'));
}
writeFile(getSrcBundle() + srcName + ".py");
Utils.zip(getSrcBundle(), zipPath);
Debug.log(2, "export to executable file: " + zipPath);
return zipPath;
}
private void writeFile(String filename) throws IOException {
this.write(new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(filename), "UTF8")));
}
public boolean close() throws IOException {
if (isDirty()) {
Object[] options = {SikuliIDEI18N._I("yes"), SikuliIDEI18N._I("no"), SikuliIDEI18N._I("cancel")};
int ans = JOptionPane.showOptionDialog(this,
SikuliIDEI18N._I("msgAskSaveChanges", getCurrentShortFilename()),
SikuliIDEI18N._I("dlgAskCloseTab"),
JOptionPane.YES_NO_CANCEL_OPTION,
JOptionPane.WARNING_MESSAGE,
null,
options, options[0]);
if (ans == JOptionPane.CANCEL_OPTION
|| ans == JOptionPane.CLOSED_OPTION) {
return false;
} else if (ans == JOptionPane.YES_OPTION) {
if (saveFile() == null) {
return false;
}
}
if (_srcBundleTemp) {
FileManager.deleteTempDir(_srcBundlePath);
}
setDirty(false);
}
return true;
}
private void setSrcBundle(String newBundlePath) {
_srcBundlePath = newBundlePath;
ImageLocator.setBundlePath(_srcBundlePath);
}
public String getSrcBundle() {
if (_srcBundlePath == null) {
File tmp = FileManager.createTempDir();
setSrcBundle(Utils.slashify(tmp.getAbsolutePath(), true));
_srcBundleTemp = true;
}
return _srcBundlePath;
}
public boolean isSourceBundleTemp() {
return _srcBundleTemp;
}
private String getCurrentShortFilename() {
if (_srcBundlePath != null) {
File f = new File(_srcBundlePath);
return f.getName();
}
return "Untitled";
}
public File getCurrentFile() {
if (_editingFile == null && isDirty()) {
try {
saveAsFile();
return _editingFile;
} catch (IOException e) {
e.printStackTrace();
}
}
return _editingFile;
}
public String getCurrentFilename() {
if (_editingFile == null) {
return null;
}
return _editingFile.getAbsolutePath();
}
private void convertSrcToHtml(String bundle) {
PythonInterpreter py = SikuliScriptRunner.getPythonInterpreter();
Debug.log(2, "Convert Sikuli source code " + bundle + " to HTML");
py.set("local_convert", true);
py.set("sikuli_src", bundle);
py.exec(pyConverter);
}
private void cleanBundle(String bundle) {
if (!PreferencesUser.getInstance().getAtSaveCleanBundle()) {
return;
}
//TODO implement in Java
PythonInterpreter py = SikuliScriptRunner.getPythonInterpreter();
Debug.log(2, "Clear source bundle " + bundle);
py.set("bundle_path", bundle);
py.exec(pyBundleCleaner);
}
public File copyFileToBundle(String filename) {
File f = new File(filename);
String bundlePath = getSrcBundle();
if (f.exists()) {
try {
File newFile = FileManager.smartCopy(filename, bundlePath);
return newFile;
} catch (IOException e) {
e.printStackTrace();
return f;
}
}
return null;
}
public File getFileInBundle(String filename) {
try {
String fullpath = ImageLocator.locate(filename);
return new File(fullpath);
} catch (IOException e) {
return null;
}
}
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="fill pane content">
@Override
public void read(Reader in, Object desc) throws IOException {
super.read(in, desc);
Document doc = getDocument();
Element root = doc.getDefaultRootElement();
parse(root);
setCaretPosition(0);
}
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="Dirty handling">
public boolean isDirty() {
return _dirty;
}
public void setDirty(boolean flag) {
if (_dirty == flag) {
return;
}
_dirty = flag;
//<editor-fold defaultstate="collapsed" desc="RaiMan no global dirty">
if (flag) {
//RaiManMod getRootPane().putClientProperty("Window.documentModified", true);
} else {
//SikuliIDE.getInstance().checkDirtyPanes();
}
//</editor-fold>
SikuliIDE.getInstance().setCurrentFileTabTitleDirty(_dirty);
}
private class DirtyHandler implements DocumentListener {
@Override
public void changedUpdate(DocumentEvent ev) {
Debug.log(9, "change update");
//setDirty(true);
}
@Override
public void insertUpdate(DocumentEvent ev) {
Debug.log(9, "insert update");
setDirty(true);
}
@Override
public void removeUpdate(DocumentEvent ev) {
Debug.log(9, "remove update");
setDirty(true);
}
}
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="Caret handling">
//TODO not used
@Override
public void caretUpdate(CaretEvent evt) {
/* seems not to be used
* if (_can_update_caret_last_x) {
* _caret_last_x = -1;
* } else {
* _can_update_caret_last_x = true;
* }
*/
}
public int getLineNumberAtCaret(int caretPosition) {
Element root = getDocument().getDefaultRootElement();
return root.getElementIndex(caretPosition) + 1;
}
public Element getLineAtCaret(int caretPosition) {
Element root = getDocument().getDefaultRootElement();
if (caretPosition == -1) {
return root.getElement(root.getElementIndex(getCaretPosition()));
} else {
return root.getElement(root.getElementIndex(root.getElementIndex(caretPosition)));
}
}
public Element getLineAtPoint(MouseEvent me) {
Point p = me.getLocationOnScreen();
Point pp = getLocationOnScreen();
p.translate(-pp.x, -pp.y);
int pos = viewToModel(p);
Element root = getDocument().getDefaultRootElement();
int e = root.getElementIndex(pos);
if (e == -1) {
return null;
}
return root.getElement(e);
}
public boolean jumpTo(int lineNo, int column) {
Debug.log(6, "jumpTo: " + lineNo + "," + column);
try {
int off = getLineStartOffset(lineNo - 1) + column - 1;
int lineCount = getDocument().getDefaultRootElement().getElementCount();
if (lineNo < lineCount) {
int nextLine = getLineStartOffset(lineNo);
if (off >= nextLine) {
off = nextLine - 1;
}
}
if (off >= 0) {
setCaretPosition(off);
}
} catch (BadLocationException ex) {
jumpTo(lineNo);
return false;
}
return true;
}
public boolean jumpTo(int lineNo) {
Debug.log(6, "jumpTo: " + lineNo);
try {
setCaretPosition(getLineStartOffset(lineNo - 1));
} catch (BadLocationException ex) {
return false;
}
return true;
}
public int getLineStartOffset(int line) throws BadLocationException {
// line starting from 0
Element map = getDocument().getDefaultRootElement();
if (line < 0) {
throw new BadLocationException("Negative line", -1);
} else if (line >= map.getElementCount()) {
throw new BadLocationException("No such line", getDocument().getLength() + 1);
} else {
Element lineElem = map.getElement(line);
return lineElem.getStartOffset();
}
}
//<editor-fold defaultstate="collapsed" desc="TODO only used for UnitTest">
public void jumpTo(String funcName) throws BadLocationException {
Debug.log(6, "jumpTo: " + funcName);
Element root = getDocument().getDefaultRootElement();
int pos = getFunctionStartOffset(funcName, root);
if (pos >= 0) {
setCaretPosition(pos);
} else {
throw new BadLocationException("Can't find function " + funcName, -1);
}
}
private int getFunctionStartOffset(String func, Element node) throws BadLocationException {
Document doc = getDocument();
int count = node.getElementCount();
Pattern patDef = Pattern.compile("def\\s+" + func + "\\s*\\(");
for (int i = 0; i < count; i++) {
Element elm = node.getElement(i);
if (elm.isLeaf()) {
int start = elm.getStartOffset(), end = elm.getEndOffset();
String line = doc.getText(start, end - start);
Matcher matcher = patDef.matcher(line);
if (matcher.find()) {
return start;
}
} else {
int p = getFunctionStartOffset(func, elm);
if (p >= 0) {
return p;
}
}
}
return -1;
}
//</editor-fold>
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="replace text patterns with image buttons">
public boolean reparse() {
File temp = FileManager.createTempFile("py");
Element e = this.getDocument().getDefaultRootElement();
if (e.getEndOffset() - e.getStartOffset() == 1) {
return true;
}
try {
writeFile(temp.getAbsolutePath());
this.read(new BufferedReader(new InputStreamReader(new FileInputStream(temp), "UTF8")), null);
updateDocumentListeners();
return true;
} catch (IOException ex) { }
return false;
}
private void parse(Element node) {
if (! showThumbs) {
// do not show any thumbnails
return;
}
int count = node.getElementCount();
for (int i = 0; i < count; i++) {
Element elm = node.getElement(i);
Debug.log(8, elm.toString());
if (elm.isLeaf()) {
int start = elm.getStartOffset(), end = elm.getEndOffset();
parseRange(start, end);
} else {
parse(elm);
}
}
}
private int parseRange(int start, int end) {
if (! showThumbs) {
// do not show any thumbnails
return end;
}
try {
end = parseLine(start, end, patCaptureBtn);
end = parseLine(start, end, patPatternStr);
end = parseLine(start, end, patRegionStr);
end = parseLine(start, end, patPngStr);
} catch (BadLocationException e) {
e.printStackTrace();
}
return end;
}
private int parseLine(int startOff, int endOff, Pattern ptn) throws BadLocationException {
if (endOff <= startOff) {
return endOff;
}
Document doc = getDocument();
while (true) {
String line = doc.getText(startOff, endOff - startOff);
Matcher m = ptn.matcher(line);
//System.out.println("["+line+"]");
if (m.find()) {
int len = m.end() - m.start();
if (replaceWithImage(startOff + m.start(), startOff + m.end(), ptn)) {
startOff += m.start() + 1;
endOff -= len - 1;
} else {
startOff += m.end() + 1;
}
} else {
break;
}
}
return endOff;
}
private boolean replaceWithImage(int startOff, int endOff, Pattern ptn)
throws BadLocationException {
Document doc = getDocument();
String imgStr = doc.getText(startOff, endOff - startOff);
JComponent comp = null;
if (ptn == patPatternStr || ptn == patPngStr) {
if (pref.getPrefMoreImageThumbs()) {
comp = EditorPatternButton.createFromString(this, imgStr, null);
} else {
comp = EditorPatternLabel.labelFromString(this, imgStr);
}
} else if (ptn == patRegionStr) {
if (pref.getPrefMoreImageThumbs()) {
comp = EditorRegionButton.createFromString(this, imgStr);
} else {
comp = EditorRegionLabel.labelFromString(this, imgStr);
}
} else if (ptn == patCaptureBtn) {
comp = EditorPatternLabel.labelFromString(this, "");
}
if (comp != null) {
this.select(startOff, endOff);
this.insertComponent(comp);
return true;
}
return false;
}
public String getRegionString(int x, int y, int w, int h) {
return String.format("Region(%d,%d,%d,%d)", x, y, w, h);
}
public String getPatternString(String ifn, float sim, Location off) {
if (ifn == null) {
return "\"" + EditorPatternLabel.CAPTURE + "\"";
}
String img = new File(ifn).getName();
String pat = "Pattern(\"" + img + "\")";
String ret = "";
if (sim > 0) {
if (sim >= 0.99F) {
ret += ".exact()";
} else if (sim != 0.7F) {
ret += String.format(Locale.ENGLISH, ".similar(%.2f)", sim);
}
}
if (off != null && (off.x != 0 || off.y != 0)) {
ret += ".targetOffset(" + off.x + "," + off.y + ")";
}
if (!ret.equals("")) {
ret = pat + ret;
} else {
ret = "\"" + img + "\"";
}
return ret;
}
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="content insert append">
public void insertString(String str) {
int sel_start = getSelectionStart();
int sel_end = getSelectionEnd();
if (sel_end != sel_start) {
try {
getDocument().remove(sel_start, sel_end - sel_start);
} catch (BadLocationException e) {
e.printStackTrace();
}
}
int pos = getCaretPosition();
insertString(pos, str);
int new_pos = getCaretPosition();
int end = parseRange(pos, new_pos);
setCaretPosition(end);
}
private void insertString(int pos, String str) {
Document doc = getDocument();
try {
doc.insertString(pos, str, null);
} catch (Exception e) {
e.printStackTrace();
}
}
//TODO not used
public void appendString(String str) {
Document doc = getDocument();
try {
int start = doc.getLength();
doc.insertString(doc.getLength(), str, null);
int end = doc.getLength();
//end = parseLine(start, end, patHistoryBtnStr);
} catch (Exception e) {
e.printStackTrace();
}
}
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="feature search">
/*
* public int search(Pattern pattern){
* return search(pattern, true);
* }
*
* public int search(Pattern pattern, boolean forward){
* if(!pattern.equals(_lastSearchPattern)){
* _lastSearchPattern = pattern;
* Document doc = getDocument();
* int pos = getCaretPosition();
* Debug.log("caret: " + pos);
* try{
* String body = doc.getText(pos, doc.getLength()-pos);
* _lastSearchMatcher = pattern.matcher(body);
* }
* catch(BadLocationException e){
* e.printStackTrace();
* }
* }
* return continueSearch(forward);
* }
*/
/*
* public int search(String str){
* return search(str, true);
* }
*/
public int search(String str, int pos, boolean forward) {
int ret = -1;
Document doc = getDocument();
Debug.log(9, "search caret: " + pos + ", " + doc.getLength());
try {
String body;
int begin;
if (forward) {
int len = doc.getLength() - pos;
body = doc.getText(pos, len > 0 ? len : 0);
begin = pos;
} else {
body = doc.getText(0, pos);
begin = 0;
}
Pattern pattern = Pattern.compile(Pattern.quote(str));
Matcher matcher = pattern.matcher(body);
ret = continueSearch(matcher, begin, forward);
if (ret < 0) {
if (forward && pos != 0) {
return search(str, 0, forward);
}
if (!forward && pos != doc.getLength()) {
return search(str, doc.getLength(), forward);
}
}
} catch (BadLocationException e) {
Debug.log(7, "search caret: " + pos + ", " + doc.getLength()
+ e.getStackTrace());
}
return ret;
}
protected int continueSearch(Matcher matcher, int pos, boolean forward) {
boolean hasNext = false;
int start = 0, end = 0;
if (!forward) {
while (matcher.find()) {
hasNext = true;
start = matcher.start();
end = matcher.end();
}
} else {
hasNext = matcher.find();
if (!hasNext) {
return -1;
}
start = matcher.start();
end = matcher.end();
}
if (hasNext) {
Document doc = getDocument();
getCaret().setDot(pos + end);
getCaret().moveDot(pos + start);
getCaret().setSelectionVisible(true);
return pos + start;
}
return -1;
}
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="Transfer code incl. images between code panes">
private class MyTransferHandler extends TransferHandler {
Map<String, String> _copiedImgs = new HashMap<String, String>();
@Override
public void exportToClipboard(JComponent comp, Clipboard clip, int action) {
super.exportToClipboard(comp, clip, action);
}
@Override
protected void exportDone(JComponent source,
Transferable data,
int action) {
if (action == TransferHandler.MOVE) {
JTextPane aTextPane = (JTextPane) source;
int sel_start = aTextPane.getSelectionStart();
int sel_end = aTextPane.getSelectionEnd();
Document doc = aTextPane.getDocument();
try {
doc.remove(sel_start, sel_end - sel_start);
} catch (BadLocationException e) {
e.printStackTrace();
}
}
}
@Override
public int getSourceActions(JComponent c) {
return COPY_OR_MOVE;
}
@Override
protected Transferable createTransferable(JComponent c) {
JTextPane aTextPane = (JTextPane) c;
EditorKit kit = ((EditorKit) aTextPane.getEditorKit());
Document doc = aTextPane.getDocument();
int sel_start = aTextPane.getSelectionStart();
int sel_end = aTextPane.getSelectionEnd();
StringWriter writer = new StringWriter();
try {
_copiedImgs.clear();
kit.write(writer, doc, sel_start, sel_end - sel_start, _copiedImgs);
return new StringSelection(writer.toString());
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
for (int i = 0; i < transferFlavors.length; i++) {
//System.out.println(transferFlavors[i]);
if (transferFlavors[i].equals(DataFlavor.stringFlavor)) {
return true;
}
}
return false;
}
@Override
public boolean importData(JComponent comp, Transferable t) {
DataFlavor htmlFlavor = DataFlavor.stringFlavor;
if (canImport(comp, t.getTransferDataFlavors())) {
try {
String transferString = (String) t.getTransferData(htmlFlavor);
EditorPane targetTextPane = (EditorPane) comp;
for (Map.Entry<String, String> entry : _copiedImgs.entrySet()) {
String imgName = entry.getKey();
String imgPath = entry.getValue();
File destFile = targetTextPane.copyFileToBundle(imgPath);
String newName = destFile.getName();
if (!newName.equals(imgName)) {
String ptnImgName = "\"" + imgName + "\"";
newName = "\"" + newName + "\"";
transferString = transferString.replaceAll(ptnImgName, newName);
Debug.info(ptnImgName + " exists. Rename it to " + newName);
}
}
targetTextPane.insertString(transferString);
} catch (Exception e) {
Debug.error("Can't transfer: " + t.toString());
}
return true;
}
return false;
}
}
//</editor-fold>
//<editor-fold defaultstate="collapsed" desc="currently not used">
private String _tabString = " ";
private void setTabSize(int charactersPerTab) {
FontMetrics fm = this.getFontMetrics(this.getFont());
int charWidth = fm.charWidth('w');
int tabWidth = charWidth * charactersPerTab;
TabStop[] tabs = new TabStop[10];
for (int j = 0; j < tabs.length; j++) {
int tab = j + 1;
tabs[j] = new TabStop(tab * tabWidth);
}
TabSet tabSet = new TabSet(tabs);
SimpleAttributeSet attributes = new SimpleAttributeSet();
StyleConstants.setFontSize(attributes, 18);
StyleConstants.setFontFamily(attributes, "Osaka-Mono");
StyleConstants.setTabSet(attributes, tabSet);
int length = getDocument().getLength();
getStyledDocument().setParagraphAttributes(0, length, attributes, true);
}
private void setTabs(int spaceForTab) {
String t = "";
for (int i = 0; i < spaceForTab; i++) {
t += " ";
}
_tabString = t;
}
private void expandTab() throws BadLocationException {
int pos = getCaretPosition();
Document doc = getDocument();
doc.remove(pos - 1, 1);
doc.insertString(pos - 1, _tabString, null);
}
private Class _historyBtnClass;
private void setHistoryCaptureButton(ButtonCapture btn) {
_historyBtnClass = btn.getClass();
}
private void indent(int startLine, int endLine, int level) {
Document doc = getDocument();
String strIndent = "";
if (level > 0) {
for (int i = 0; i < level; i++) {
strIndent += " ";
}
} else {
Debug.error("negative indentation not supported yet!!");
}
for (int i = startLine; i < endLine; i++) {
try {
int off = getLineStartOffset(i);
if (level > 0) {
doc.insertString(off, strIndent, null);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private void checkCompletion(java.awt.event.KeyEvent ke) throws BadLocationException {
Document doc = getDocument();
Element root = doc.getDefaultRootElement();
int pos = getCaretPosition();
int lineIdx = root.getElementIndex(pos);
Element line = root.getElement(lineIdx);
int start = line.getStartOffset(), len = line.getEndOffset() - start;
String strLine = doc.getText(start, len - 1);
Debug.log(9, "[" + strLine + "]");
if (strLine.endsWith("find") && ke.getKeyChar() == '(') {
ke.consume();
doc.insertString(pos, "(", null);
ButtonCapture btnCapture = new ButtonCapture(this, line);
insertComponent(btnCapture);
doc.insertString(pos + 2, ")", null);
}
}
//</editor-fold>
}